/*
 * Programmed by Víctor Egea Hernando
 * egea.hernando@gmail.com (06-03-11)
 * Abas Ibérica.
 *
 * This program comes without any WARRANTY!
 * But you can text me to ask any question about it
 */
 
#include "EasyFOP.h"

#include <ctime>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>

EasyFOP *EasyFOP::_instance = 0;

// Drive
std::string EasyFOP::DRIVE = "C";
// PSCP path
const char *EasyFOP::PSCP = ":\\EASYFOP\\PUTTY\\PSCP.EXE";
// PLINK path
const char *EasyFOP::PLINK = ":\\EASYFOP\\PUTTY\\PLINK.EXE";
// DOS2UNIX path
const char *EasyFOP::DOS2UNIX = ":\\EASYFOP\\CONV\\DOS2UNIX.EXE";

// FILE END OF LINE ( LF )
static std::string FEOL = "\u000A";
// WINDOWS END OF LINE ( CR + LF )
static std::string WEOL = "\n";

EasyFOP::EasyFOP(){
	_filename = "";
	_dirname = ""; 
	_spxfile = "";
	_labelnum = 1;
	_comp_oldgen = false;
}

void EasyFOP::print_program_info( void ){
	std::cout << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
	std::cout << " easyfop 0.1" << std::endl << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 10 );
	std::cout << "  /\\ |_  _. _   | |_  _ ._ o _ _." << std::endl;
	std::cout << " /--\\|_)(_|_>  _|_|_)(/_|  |(_(_|" << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
	std::cout << std::endl;
	std::cout <<" SPX2FOP translator enhanced with SSH" << std::endl << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 128 );
	std::cout << "Programed by:    Victor Egea Hernando   " << std::endl;
	std::cout << "Contact through: egea.hernando@gmail.com" << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
}

void EasyFOP::print_help( void ){
	std::cout << std::endl;
	std::cout << "Usage: easyfop [options] xfopfile" << std::endl;
	std::cout << std::endl;
	std::cout << "  -s   server[:port] server and port" << std::endl;
	std::cout << "  -u   user          SSH user" << std::endl;
	std::cout << "  -p   password      SSH password" << std::endl;
	std::cout << "  -d   drive         Windows drive" << std::endl;
	std::cout << "  -pk                Use private key" << std::endl;
	std::cout << "  -cma               Compatibility mode A" << std::endl;
	std::cout << "                     easy2lg & easy2eks" << std::endl;
}

void EasyFOP::translate_phase0(){
	std::fstream in( _file.c_str(), std::ios::in | std::ios::binary ); 
	_file += ".TMP"; // rename the new file
	std::fstream out( _file.c_str(), std::ios::out | std::ios::binary );
	// check files
	if( !in.is_open() || !out.is_open() ){
		std::cerr << "Error changing scape sequences" << std::endl;
		exit(1);
	}
	
	// translate lines one by one
	do{
		char line[ 4096 ];
		// read line	
		in.getline( line, 4096 );
		std::string linestr = line;
		// replace strings '&' -> '&amp;'
		int start = 0;
		while( start != std::string::npos ){
			start = linestr.find( "&", start );
			if( start != std::string::npos ){
				linestr.replace( start, 1, "&amp;" );
				start += 5;
			}
		}
		// replace strings '<' -> '&lt;'
		start = 0;
		while( start != std::string::npos ){
			start = linestr.find( "<", start );
			if( start != std::string::npos ){
				linestr.replace( start, 1, "&lt;" );
				start += 4;
			}
		}
		// replace strings '>' -> '&gt;'
		start = 0;
		while( start != std::string::npos ){
			start = linestr.find( ">", start );
			if( start != std::string::npos ){
				linestr.replace( start, 1, "&gt;" );
				start += 4;
			}
		}
		// replace strings '@@{@@' -> '<'
		start = 0;
		while( start != std::string::npos ){
			start = linestr.find( "@@{@@", start );
			if( start != std::string::npos ){
				linestr.replace( start, 5, "<" );
				start += 1;
			}
		}
		// replace strings '@@}@@' -> '>'
		start = 0;
		while( start != std::string::npos ){
			start = linestr.find( "@@}@@", start );
			if( start != std::string::npos ){
				linestr.replace( start, 5, ">" );
				start += 1;
			}
		}
		// write line in new file
		linestr += "\n";
		out.write( linestr.c_str(), linestr.length() );
	}while( !in.eof() );
	// close output file
	out.close();
}

void EasyFOP::translate_phase1( pugi::xml_node csibling ){
	// get siblings
	while( csibling ){
		// PROCESS NODE
		if( !std::string( csibling.name()).compare("XFOPPROC") ){
			std::string name = csibling.attribute("NAME").value();
			// check if the procedure name is empty
			if( name.empty() ){
				SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
				std::cerr << "** A PROCEDURE NAME IS EMPTY **" << std::endl;
				SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
				std::exit( 1 );
			}
			// check if it's repeated
			int procnum = get_proc_num( name );
			if( procnum > 0 ){
				SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
				std::cerr << "** THE PROCEDURE NAME \"" << name << "\" IS REPEATED TWICE **" << std::endl;
				SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
				std::exit( 1 );
			}
			// add procedure
			_proc.push_back( name );
			std::cout << ">> [Phase 1] Process \"" << name;
			std::cout << "\" got with index " << _proc.size() << "." << std::endl;
		}else if( !std::string( csibling.name() ).compare("XFOPCALL") ){
			if( _comp_oldgen ){
				std::string name = csibling.child_value();
				// get procedure name
				int procnum = get_proc_num( name );
				if( procnum < 0 ){
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << ">> COMPATIBILITY MODE A: PROCEDURE IS CALLED BEFORE DEFINITION" << std::endl;
					std::cerr << "** THE PROCEDURE CALL \"" << name << "\" WASN'T FOUND **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}
			}
		}else{
			// get value
			std::string value = csibling.child_value();
			// search for "Pfad"
			int pos1 = value.find( "Pfad" );
			if( _dirname.empty() && pos1 >= 0 ){
				int pos4 = value.find_first_of( ':',  pos1 );
				int pos5 = value.find_first_of( '\n', pos4 );
				if( pos4 >= 0 ){
					if( pos5 ){
						_dirname = value.substr( pos4 + 1, pos5 - pos4 - 1 );
					}else{
						_dirname = value.substr( pos4 + 1 );
					}
					// trim name
					_dirname = _dirname.erase( _dirname.find_last_not_of( ' ' ) + 1 );
					_dirname = _dirname.erase( 0 , _dirname.find_first_not_of( ' ' ) );
					std::cout << ">> [Phase 1] Got workspace: \"" << _dirname << "\"." << std::endl; 
				}
			}
			// search for "Name"
			int pos2 = value.find( "Name" );
			if( _filename.empty() && pos2 >= 0 ){
				int pos4 = value.find_first_of( ':',  pos2 );
				int pos5 = value.find_first_of( '\n', pos4 );
				if( pos4 >= 0 ){
					if( pos5 ){
						_filename = value.substr( pos4 + 1, pos5 - pos4 - 1 );
					}else{
						_filename = value.substr( pos4 + 1 );
					}
					// trim name
					_filename = _filename.erase( _filename.find_last_not_of( ' ' ) + 1 );
					_filename = _filename.erase( 0 , _filename.find_first_not_of( ' ' ) );
					std::cout << ">> [Phase 1] Got FOP-name : \"" << _filename << "\"." << std::endl;
				}
			}
			// search for "file"
			int pos3 = value.find( "file" );
			if( _spxfile.empty() && pos3 >= 0 ){
				int pos4 = value.find_first_of( ':',  pos3 );
				int pos5 = value.find_first_of( '\n', pos4 );
				if( pos4 >= 0 ){
					if( pos5 ){
						_spxfile = value.substr( pos4 + 1, pos5 - pos4 - 1 );
					}else{
						_spxfile = value.substr( pos4 + 1 );
					}
					// trim name
					_spxfile = _spxfile.erase( _spxfile.find_last_not_of( ' ' ) + 1 );
					_spxfile = _spxfile.erase( 0 , _spxfile.find_first_not_of( ' ' ) );
					std::cout << ">> [Phase 1] Got SPX-file : \"" << _spxfile << "\"." << std::endl; 
				}
			}
		}
		// END PROCESS NODE

		// get values from childs
		translate_phase1( csibling.first_child() );
		
		// next sibling
		csibling = csibling.next_sibling();
	}
}

void EasyFOP::translate_phase2( pugi::xml_node csibling, std::fstream &stream, 
	unsigned int loopend, unsigned int case_end, const std::string &segment ){
	// last labelnum used by a sibling
	unsigned int labelnum = 0;  
	// case variable & value
	std::string casevar = "", caseval = "";
	// otherwise case was reached
	bool otherwise = false;
	// get siblings
	while( csibling ){
		// PROCESS NODE
			if( !std::string( csibling.name() ).compare("XFOPPROC") ){
				std::string name = csibling.attribute("NAME").value();
				// get procedure name
				int procnum = get_proc_num( name );
				stream << "!PRC"; print_labnumber( stream, procnum, false );
				stream << ": ----- " << name << " -----"  << FEOL;
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end );
				stream << ".continue 'xprocst.xprocsp'" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPCALL") ){
				std::string name = csibling.child_value();
				labelnum = _labelnum; // get current label number
				_labelnum ++; // reserve range for the next labels
				// check errors
				if( name.empty() ){
					std::cerr << segment << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << "** A PROCEDURE CALL IS EMPTY **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}
				// get procedure name
				int procnum = get_proc_num( name );
				// check errors
				if( procnum < 0 ){
					std::cerr << segment << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << "** THE PROCEDURE CALL \""; 
					std::cerr << name << "\" WASN'T FOUND **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}
				// generate code
				stream << ".. --- CALLING: " << name << " ---" << FEOL; 
				stream << ".fo xprocsp = xprocsp + 1" << FEOL;
				stream << ".type text xprocst.xprocsp ? _defined( xprocst.xprocsp )";
				stream << FEOL << ".fo xprocst.xprocsp = \"LAB";
				print_labnumber( stream, labelnum, false ); stream << "\"" << FEOL;
				stream << ".continue PRC"; print_labnumber( stream, procnum );
				stream << "!LAB"; print_labnumber( stream, labelnum );
				stream << ".fo xprocsp = xprocsp - 1" << FEOL;
				stream << ".. --- END CALLING: " << name << " ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPSTATEMENT") ){
				std::string value = csibling.child_value();
				if( value[ value.length() - 1 ] == '\n' ){
					stream << value;
				}else{
					stream << value << FEOL;
				}
			}else if( !std::string( csibling.name() ).compare("XFOPSEGMENT") ){
				// get segment
				std::string upsegment = ">> [Phase 2] Segment \"";
				std::string svalue = std::string( csibling.child_value() ).substr( 3, 53 );
				int pos = svalue.find_first_of( "\n" );
				upsegment = upsegment + ( ( !pos ) ? svalue : svalue.substr( 0, pos ) ) + "\".";
				// get value
				std::string value = csibling.child_value();
				if( value[ value.length() - 1 ] == '\n' ){
					stream << value;
				}else{
					stream << value << FEOL;
				}
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end, upsegment );
			}else if( !std::string( csibling.name() ).compare("XFOP") ){
				stream << ".. " << csibling.child_value();
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end );
			}else if( !std::string( csibling.name() ).compare("XFOPWHILE") ){
				labelnum = _labelnum; // get current label number
				_labelnum += 2; // reserve range for the next labels
				stream << "!LAB"; print_labnumber( stream, labelnum, false );
				stream << ": --- WHILE LOOP ---" << FEOL;
				// translate childs
				translate_phase2( csibling.first_child(), stream, labelnum + 1, case_end, segment );
				stream << ".continue LAB"; print_labnumber( stream, labelnum );
				stream << "!LAB"; print_labnumber( stream, labelnum + 1, false );
				stream << ": --- WHILE END ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPREPEAT") ){
				labelnum = _labelnum; // get current label number
				_labelnum += 2; // reserve range for the next labels
				stream << "!LAB"; print_labnumber( stream, labelnum, false );
				stream << ": --- REPEAT LOOP ---" << FEOL;
				// translate childs
				translate_phase2( csibling.first_child(), stream, labelnum + 1, case_end, segment );
				stream << "!LAB"; print_labnumber( stream, labelnum + 1, false );
				stream << ": --- REPEAT END ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPLOOP") ){
				if( _comp_oldgen ){
					std::cerr << segment << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << "** INFINITE LOOP NOT ALLOWED IN COMPATIBILITY MODE A **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}
				labelnum = _labelnum; // get current label number
				_labelnum += 2; // reserve range for the next labels
				stream << "!LAB"; print_labnumber( stream, labelnum, false );
				stream << ": --- INFINITE LOOP ---" << FEOL;
				// translate childs
				translate_phase2( csibling.first_child(), stream, labelnum + 1, case_end, segment );
				stream << ".continue LAB"; print_labnumber( stream, labelnum );
				stream << "!LAB"; print_labnumber( stream, labelnum + 1, false );
				stream << ": --- INFINITE LOOP END ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPIF") ){
				std::string kind = csibling.attribute("KIND").value();
				// reserve a label
				if( !kind.compare("IF") ){
					labelnum = _labelnum; // get current label number
					_labelnum += 2; // reserve range for the next labels
				}
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end, segment );
				// if end label
				if( !kind.compare("IF") ){
					stream << ".continue LAB"; print_labnumber( stream, labelnum );
					stream << "!LAB"; print_labnumber( stream, labelnum + 1, false );
					stream << ": --- ELSE ENTRY ---" << FEOL;
				}else if( !kind.compare("ELSE") ){
					stream << "!LAB"; print_labnumber( stream, labelnum, false );
					stream << ": --- IF END ---" << FEOL;
				}
			}else if( !std::string( csibling.name() ).compare("XFOPCASEVAR") ){
				// get case variable
				casevar = csibling.child_value();
				stream << ".. --- CASE FOR VARIABLE: \"" << casevar << "\" ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPCASEOF") ){
				labelnum = _labelnum; // get current label number
				_labelnum ++; // reserve range for the next labels
				// get case value
				caseval = csibling.child_value();
				stream << ".continue LAB"; print_labnumber( stream, labelnum, false );
				stream << " ? ( " << casevar << " <> " << caseval + " )" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPCASEBODY") ){
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end, segment );
				if( !otherwise ){
					stream << ".continue LAB"; print_labnumber( stream, case_end );
					stream << "!LAB"; print_labnumber( stream, labelnum, false );
					stream << ": --- CASE END FOR VALUE: " << caseval << " ---" << FEOL;
				}
			}else if( !std::string( csibling.name() ).compare("XFOPCASEOTHERWISE") ){
				// mark as otherwise reached
				otherwise = true;
				stream << ".. --- CASE OTHERWISE ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPCASE") ){
				labelnum = _labelnum; // get current label number
				_labelnum ++; // reserve range for the next labels
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, labelnum, segment );
				stream << "!LAB"; print_labnumber( stream, labelnum, false );
				stream << ": --- CASE END ---" << FEOL;
			}else if( !std::string( csibling.name() ).compare("XFOPCONDITION") ){
				std::string kind = csibling.attribute("KIND").value();
				if( !kind.compare("REPEAT") ){
					stream << ".continue LAB"; 
					print_labnumber( stream, loopend - 1, false );
					stream << " ? _( " << csibling.child_value() << " )" << FEOL;
				}else if( !kind.compare( "WHILE" ) ){
					stream << ".continue LAB"; 
					print_labnumber( stream, loopend, false );
					stream << " ? _( " << csibling.child_value() << " )" << FEOL;
				}else if( !kind.compare( "IF" ) ){
					stream << ".continue LAB"; 
					print_labnumber( stream, _labelnum - 1, false );
					stream << " ? _( " << csibling.child_value() << " )" << FEOL;
				}
			}else if( !std::string( csibling.name() ).compare("XFOPEXIT") ){
				if( _comp_oldgen ){
					std::cerr << segment << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << "** LOOP EXIT NOT ALLOWED IN COMPATIBILITY MODE A **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}else if( !loopend ){
					std::cerr << segment << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
					std::cerr << "** YOU CAN NOT EXIT WITHIN NO-LOOP!! **" << std::endl;
					SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
					std::exit( 1 );
				}
				// translate exit
				stream << ".. --- LOOP EXIT ---" << FEOL;
				stream << ".continue LAB"; 
				print_labnumber( stream, loopend, false );
				stream << " ? ( " << csibling.child_value() << " )" << FEOL;
			}else{
				// translate childs
				translate_phase2( csibling.first_child(), stream, loopend, case_end, segment );
			}
		// END PROCESS NODE
		
		// next sibling
		csibling = csibling.next_sibling();
	}
}

void EasyFOP::print_labnumber( std::fstream &stream, unsigned int labnum, bool eol ){
	stream.fill('0'); stream.width( 4 );
	stream << std::right << labnum;
	if( eol ) stream << FEOL;
}

int EasyFOP::get_proc_num( const std::string &str ){
	// get the procecure number/index
	for( int i = 0; i < _proc.size(); i++ ){
		if( !_proc[ i ].compare( str ) ){
			return ++i; 
		}
	}
	// the procedure wasn't found
	return -1;
}

void EasyFOP::compile_fop( void ){
	translate_phase0();
	// load XFOP
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file( _file.c_str() );
	std::cout << std::endl << "XFOP-FILE: \"" << _file << "\"" << std::endl;
	// check errors
	if( !result ){
		std::cerr << "Error loading XFOP" << std::endl;
		std::cerr << "XML related error: " << result.description() << std::endl;
		exit( 1 );
	}
	// create FOP stream
	std::stringstream fopstreamstr;
	fopstreamstr << DRIVE << ":\\EASYFOP\\GEN\\" << _user << "@" << _server;
	std::string directory = fopstreamstr.str(); srand ( time(NULL) ); 
	
	// Create random file name
	std::stringstream fopstreamstr2; 
	fopstreamstr2 << rand();
	_randfop = fopstreamstr2.str();
	
	// Assign fopstreamstr
	fopstreamstr << "\\" << _randfop  << ".fop";
	_foppath = fopstreamstr.str();
	
	std::fstream fopstream( _foppath.c_str(), std::ios::binary | std::ios::out );
	// check creation of the file
	if( !fopstream.is_open() ){
		if( !CreateDirectory( directory.c_str(), NULL ) ){
			std::cerr << "Error creating FOP directory" << std::endl;
			exit(1);
		}else{
			fopstream.open( _foppath.c_str(), std::ios::out );
			// check file
			if( !fopstream.is_open() ){
				std::cerr << "Error creating FOP file" << std::endl;
				exit(1);
			}
		}
	}
	// go through DOM
	std::cout << "FOP-PATH : \"" << _foppath << "\"" << std::endl;
	std::cout << std::endl;
	std::cout << "The XFOP file is being translated to FOP...";
	std::cout << std::endl << std::endl;
	// Translate
	translate_phase1( doc );
	translate_phase2( doc, fopstream, 0, 0 );
	std::cout << std::endl;
	// ...
	fopstream.close();
    // convert FOP a UNIX
    execute_program( std::string( DRIVE ) + DOS2UNIX, _foppath.c_str() );
	// save SPX 
	save_spx();
	// OK!!!!!
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 10 );
	std::cout << "A backup of the SPX was created!" << std::endl;
	std::cout << "The file was processed successfully!!" << std::endl << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
}

void EasyFOP::save_spx( void ){
	std::stringstream fopstreamstr; std::stringstream fopstreamstr2;
	fopstreamstr << DRIVE << ":\\EASYFOP\\HIST\\" << _user << "@" << _server << "\\";
	
	// Get complete path to SPX
	std::string dirname = _dirname;int pos1 = 0;
	do{
		pos1 = dirname.find_first_of( "./", pos1 );
		if( pos1 >= 0 ){
			dirname = dirname.replace( pos1, 1, "" );
		}
	}while( pos1 >= 0 );
	
	fopstreamstr2 << fopstreamstr.str() << dirname << "\\";
	fopstreamstr2 << _filename << "\\" << _randfop << ".spx";
	std::string foppath = fopstreamstr2.str(); 
	// first try
	std::fstream fopstream;
	fopstream.open( foppath.c_str(), std::ios::out | std::ios::binary );
	// check creation of the file
	if( !fopstream.is_open() ){
		CreateDirectory( fopstreamstr.str().c_str(), NULL );
	}
	fopstream.close();
	fopstream.open( foppath.c_str(), std::ios::out | std::ios::binary );
	if( !fopstream.is_open() ){
		fopstreamstr << dirname << "\\";
		CreateDirectory( fopstreamstr.str().c_str(), NULL );
	}
	fopstream.close();
	fopstream.open( foppath.c_str(), std::ios::out | std::ios::binary );
	if( !fopstream.is_open() ){
		fopstreamstr << _filename << "\\";
		CreateDirectory( fopstreamstr.str().c_str(), NULL );
	}

	fopstream.close();
	fopstream.open( foppath.c_str(), std::ios::out | std::ios::binary );
	if( !fopstream.is_open() ){
		SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
		std::cerr << "Error creating SPX copy: \"" << foppath << "\"" << std::endl;
		SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
		exit(1);
	}else{
		// copy file
		std::fstream origstream( _spxfile.c_str(), std::ios::in | std::ios::binary );
		// check file opening
		if( !origstream.is_open() ){
			std::cerr << "Error reading original SPX: \"" << origstream << "\"" << std::endl;
		}else{
			// copy SPX
			char *data = new char[ 1<<11 ]; // 4 KB
			while( !origstream.eof() ){
				origstream.read( data, 1<<11 );
				fopstream.write( data, origstream.gcount() );
			} 
			fopstream.close(); origstream.close();
			delete data;
		}
	}

}

void EasyFOP::send_fop( void ){
	// PSCP arguments
	std::string args1 = "-P " + _port;
	if( !_private_key ){
		args1 += " -pw " + ( ( !_pw.empty() ) ? _pw : _user );
	}else{
		args1 += std::string( " -i \"" ) + DRIVE + ":\\EASYFOP\\PKEYS\\";
		args1 += _user + "@" + _server + ".key\"";
	}
	args1 += " \"" + _foppath +  + "\" " + _user + "@" + _server;
	args1 += ":" + _dirname + "/" + _filename;
	// PLINK arguments
	std::string args2 = "-P " + _port;
	if( !_private_key ){
		args2 += " -pw " + ( ( !_pw.empty() ) ? _pw : _user );
	}else{
		args2 += std::string( " -i \"" ) + DRIVE + ":\\EASYFOP\\PKEYS\\";
		args2 +=  _user + "@" + _server + ".key\"";
	}
	args2 += " " + _user + "@" + _server;
	args2 += " chmod 664 " + _dirname + "/" + _filename;
	// show command to run
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 11 );
	std::cout << " $ ";
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 15 );
	std::cout << DRIVE << std::string( PSCP ) << " " << args1 << std::endl;
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
	// execute commands
	if( execute_program( DRIVE + PSCP, args1 ) ){
		std::cerr << "Error running: " + std::string( PSCP ) + " " + args1;
		exit(1);
	}else{ 
		// show command to run
		SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 11 );
		std::cout << " $ ";
		SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 15 );
		std::cout << DRIVE << std::string( PLINK ) << " " << args2 << std::endl;
		SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 );
		if( execute_program( DRIVE + PLINK, args2 ) ){
			std::cerr << "Error running: " + std::string( PLINK ) + " " + args2;
			exit(1);
		}
	}
	SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 7 );
}

int EasyFOP::execute_program( const std::string &program, const std::string &args ){
	std::cout << std::endl;
	// create structures
    PROCESS_INFORMATION processInformation; STARTUPINFO startupInfo;
    memset( &processInformation, 0, sizeof( processInformation ) );
    memset( &startupInfo, 0, sizeof( startupInfo ) );
    startupInfo.cb = sizeof( startupInfo );
	// execute program
	int ret = CreateProcess( program.c_str(), (char*)( program + " " + args ).c_str(), 
		NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL,
		&startupInfo, &processInformation );
	// wait for process
	int res;
	do {
		Sleep( 100 );
		res = WaitForSingleObject( processInformation.hProcess, 10 );
	} while ( res == WAIT_TIMEOUT );
	
	std::cout << std::endl;
	// return code
	return !ret;
}

EasyFOP& EasyFOP::get( void ){
	if( !_instance ) 
		_instance = new EasyFOP();
	return *_instance;
}

int EasyFOP::main( int argc, char *argv[] ){
	print_program_info();
	// cheack arguments
	if( argc <= 1 ){
		print_help();
	}else{
		_server = "atlas"; _port = "22";
		// get for arguments
		for(int idx=1;idx<argc;idx++){
			if( !std::string(argv[idx]).compare("-h") ){
				print_help();
			}else if( !std::string(argv[idx]).compare("-s") ){
				idx++; // get server
				if( idx > argc - 1 ){
					std::cerr << std::endl;
					std::cerr << "Error: server missing"<< std::endl;
					exit(1);
				}
				_server = argv[idx];
				// split port from server address
				int sidx = _server.find_first_of(":");
				if( sidx != std::string::npos ){
					_port = _server.substr( sidx + 1, _server.length() - 1 );
					_server = _server.substr( 0, sidx );
				}
			}else if( !std::string(argv[idx]).compare("-u") ){
				idx++; // get username
				if( idx > argc - 1 ){
					std::cerr << std::endl;
					std::cerr << "Error: username missing"<< std::endl;
					exit(1);
				}
				_user = argv[idx]; // if( _pw.empty() ) _pw = _user;
			}else if( !std::string(argv[idx]).compare("-p") ){
				idx++; // get password
				if( idx > argc - 1 ){
					std::cerr << std::endl;
					std::cerr << "Error: password missing"<< std::endl;
					exit(1);
				}else{
					if( _private_key ){
						std::cout << std::endl;
						std::cout << "Private key will be used as identification!" << std::endl;
					}
				}
				_pw = argv[idx];
			}else if( !std::string(argv[idx]).compare("-d") ){
				idx++; // get drive
				if( idx > argc - 1 ){
					std::cerr << std::endl;
					std::cerr << "Error: drive is missing"<< std::endl;
					exit(1);
				}
				DRIVE = argv[idx];
			}else if( !std::string(argv[idx]).compare("-cma") ){
				_comp_oldgen = true;
			}else if( !std::string(argv[idx]).compare("-pk") ){
				if( !_pw.empty() ){
					std::cerr << std::endl;
					std::cerr << "A password was given!" << std::endl;
					exit(1);
				}
				_private_key = true;
			}else{	
				// get xfop and try to execute program
				if( idx == argc - 1 ){
					_file = argv[idx];
					if( !_user.empty() ){
						// execute program
						compile_fop(); send_fop();
						exit(0);
					}else{
						std::cerr << std::endl;
						std::cerr << "Error: username missing"<< std::endl;
						print_help();
						exit(1);
					}
				}else{
					std::cerr << std::endl;
					std::cerr << "Error: unknown argument: " << argv[idx] << std::endl;
					exit(1);
				}
			}
		}
		
		// arguments missing
		print_help();
		exit(1);
	}
}

int main( int argc, char *argv[] ){
	EasyFOP::get().main( argc, argv );
}
